package ye.guo.huang.receive.service.impl;

import cn.hutool.log.Log;
import cn.hutool.log.level.Level;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import ye.guo.huang.datatransferdemo.model.RequestData;
import ye.guo.huang.datatransferdemo.model.ResponseResult;
import ye.guo.huang.datatransferdemo.utils.AESUtils;
import ye.guo.huang.datatransferdemo.utils.SignUtils;
import ye.guo.huang.receive.aop.Permission;
import ye.guo.huang.receive.service.PermissionService;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Service
public class PermissionServiceImpl implements PermissionService {

	private static final Long timeOut = 300000L;

	private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> book = new ConcurrentHashMap();

	@Override
	public Object action(ProceedingJoinPoint pjp, Permission permission) {
		Object[] args = pjp.getArgs();
		String jsonStr = JSON.toJSONString(args[0]);
		RequestData requestData = (RequestData) JSONObject.parseObject(jsonStr, RequestData.class);
		Log.get().log(Level.INFO,"对接原始密文 requestData==>" + requestData);
		RequestAttributes ra = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes sra = (ServletRequestAttributes)ra;
		HttpServletRequest request = sra.getRequest();
		String requestURI = request.getRequestURI();
		if (this.limitRequest(requestData.getAppid(), permission, request.getRequestURI())) {
			return ResponseResult.fail(5003, "请求过于频繁");
		}
//		else if (requestData.getTimestamp() + timeOut < System.currentTimeMillis()) {
//			return ResponseResult.fail(5002, "请求过期");
//		}
		else if (StringUtils.isEmpty(requestData.getSign())) {
			return ResponseResult.fail(5004, "非法应用（签名不能为空）");
		} else {
			//todo  appId校验，校验方案由实际业务决定 此处只进行判空
			String appid = requestData.getAppid();
			if (StringUtils.isEmpty(appid)) {
				return ResponseResult.fail(5004, "非法应用（appid错误）");
			}
			//todo  秘钥的获取，校验  由实际业务决定  此处先写死   52226dac949c41ada5552ce3b2b69840  949c41ada5552ce3
			String secret = appid.substring(8,24);
			if (StringUtils.isBlank(secret)) {
				return ResponseResult.fail(5004, "非法应用（秘钥不存在）");
			} else {
				String sign = SignUtils.getMD5(requestData.sortData(secret), 32).toLowerCase();
				if (!sign.equals(requestData.getSign())) {
					return ResponseResult.fail(5001, "sign值校验不通过");
				} else {
					String data = requestData.getData();
					if (StringUtils.isNotBlank(data)) {
//						String json = "Bg4Io1c3yTfmUKiJt1WFhfV8HCQXnKiWYXUYa81SpcSUsRyB7ToNfnB5d9hbcC3ooe2hYuqbwSN5\n/hBlWpMEtuaL1tky5YresNLqXPw1FjhlneSg6COVrd59YfIlI2JZFhqGjozkVxu8vBK0vldUR0z1\nv6uT24mZ8LB21rzm3iOlVWPFfX0CHINsD714ehlZHVvz6LmzlNyH5/1cfoJGh4pfZ0hvrEbfWNx7\nRm2jWcI=" ;
						//实际生产中 有些系统产出的密文存在大量换行符  保险起见，先替换
						data = data.replaceAll("\n","");
						String param = AESUtils.Decrypt(data, secret);
						Log.get().log(Level.INFO,"对接明文 param==>" + param);
						if (StringUtils.isBlank(param)) {
							return ResponseResult.fail(5007, "解秘失败");
						}
						//把参数写进去
						requestData.setParam(param);
					}
					try {
						//解密后的参数写入对应的controller方法
						args[0] = requestData;
						Object proceed = pjp.proceed(args);
						return proceed;
					} catch (Throwable e) {
						Log.get().log(Level.INFO,"对接 aop 报错==>" + e);
						e.printStackTrace();
						return e;
					}
				}
			}
		}
	}

	/**
	 * 	限制流量
	 * @param appId
	 * @param permission
	 * @param url
	 * @return
	 */
	private Boolean limitRequest(String appId, Permission permission, String url) {
		ExpiringMap<String, Integer> uc = (ExpiringMap)book.getOrDefault(url, ExpiringMap.builder().variableExpiration().build());
		Integer uCount = (Integer)uc.getOrDefault(appId, 0);
		if (uCount >= permission.count()) {
			return true;
		} else {
			if (uCount == 0) {
				uc.put(appId, uCount + 1, ExpirationPolicy.CREATED, permission.time(), TimeUnit.MILLISECONDS);
			} else {
				uc.put(appId, uCount + 1);
			}

			book.put(url, uc);
			return false;
		}
	}
}
